//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2005 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////
//
//
// (C) Copyright 2007 Marvell International Ltd.
//  All Rights Reserved
//

#define SHUTDOWN_TIMEOUT                  90000
#define FLIGHTMODE_TIMEOUT                30000
#define POWER_LOCK_TIMEOUT                (SHUTDOWN_TIMEOUT + FLIGHTMODE_TIMEOUT)
#define IDLE_TIMER_RESET_CYCLE            10000
#define ENABLEURC_RETRIES                 2
#define ENABLEURC_RETRIES_DELAY           3000
#define WAIT_FOR_START_TIMEOUT            30000
#define MIN_DSB_DTR_LOW_TIME              2000
#define PWR_QUEUE_SIZE                    5

// Array with baud rates to be scanned during baud rate synchronization.
// The first zero is the placeholder for the required baud rate filled
// by the scanning algorithm.
#define BAUDRATES_TO_SCAN                 { 0, 115200, 230400, 57600, 38400 }

#define FLIGHTMODE_CMD_ON                 "AT^SCFG=\"MEopMode/Airplane\",\"006F006E\"\r"
#define FLIGHTMODE_CMD_OFF                "AT^SCFG=\"MEopMode/Airplane\",\"006F00660066\"\r"
#define FLIGHTMODE_START_ON               "AT^SCFG=\"MEopMode/Airplane/OnStart\",\"006F006E\"\r"
#define FLIGHTMODE_START_OFF              "AT^SCFG=\"MEopMode/Airplane/OnStart\",\"006F00660066\"\r"
#define FLIGHTMODE_CMD_QUERY              "AT^SCFG=\"MEopMode/Airplane\"\r"
#define FLIGHTMODE_START_QUERY            "AT^SCFG=\"MEopMode/Airplane/OnStart\"\r"
#define FLIGHTMODE_CMD_RESPONSE_ON        "\"MEopMode/Airplane\",\"006F006E\""
#define FLIGHTMODE_CMD_RESPONSE_OFF       "\"MEopMode/Airplane\",\"006F00660066\""
#define FLIGHTMODE_START_RESPONSE_ON      "\"MEopMode/Airplane/OnStart\",\"006F006E\""
#define FLIGHTMODE_START_RESPONSE_OFF     "\"MEopMode/Airplane/OnStart\",\"006F00660066\""
#define FLIGHTMODE_CMD(fOnOff)            (fOnOff ? FLIGHTMODE_CMD_ON : FLIGHTMODE_CMD_OFF)
#define FLIGHTMODE_CMD_RESPONSE(fOnOff)   (fOnOff ? FLIGHTMODE_CMD_RESPONSE_ON : FLIGHTMODE_CMD_RESPONSE_OFF)
#define FLIGHTMODE_START(fOnOff)          (fOnOff ? FLIGHTMODE_START_ON : FLIGHTMODE_START_OFF)
#define FLIGHTMODE_START_RESPONSE(fOnOff) (fOnOff ? FLIGHTMODE_START_RESPONSE_ON : FLIGHTMODE_START_RESPONSE_OFF)

#define DEFAULT_URCCONFIG                 MODEMSTATE_SIEMENS_URCCONFIG_CALL_SMS
#define INVALID_MODEM_STATE               (MODEMSTATE_SIEMENS_MAX + 1)
#define IS_VALID_MODEMSTATE(State)        ((State >= MODEMSTATE_SIEMENS_MIN) && (State <= MODEMSTATE_SIEMENS_MAX))
#define IS_URCSTATE(State)                (IS_URCCONFIG(State) || (State == MODEMSTATE_SIEMENS_POWERON))
#define IS_URCCONFIG(State)               ((State == MODEMSTATE_SIEMENS_URCCONFIG_CALL) || \
                                           (State == MODEMSTATE_SIEMENS_URCCONFIG_CALL_SMS) || \
                                           (State == MODEMSTATE_SIEMENS_URCCONFIG_CALL_SMS_REG))
#define PWR_STATE_TXT(State)              TEXT(#State)

// When polling with AT+CSQ is activated, the notifications have to be turned off.
#ifdef RIL_USE_CSQPOLLING
#ifdef RIL_USE_CSQ_NOTIFICATIONS
#undef RIL_USE_CSQ_NOTIFICATIONS
#endif
#endif

class CRilInstanceHandle;
class CRilHandle;

//////////////////////////////////////////////////////////////////////////////
// Class for power commands
//////////////////////////////////////////////////////////////////////////////
class CPowerCmd : public CCommand {
public:
  CPowerCmd(DWORD dwCmd, CRilInstanceHandle* pHandle, CEDEVICE_POWER_STATE NewPwrState, CEDEVICE_POWER_STATE OldPwrState, BOOL fEquCmd, BOOL fUrcInit);
  ~CPowerCmd();
  DWORD                 GetCmd(void) { return m_dwCmd; };
  CEDEVICE_POWER_STATE  GetNewPowerState(void) { return m_NewPwrState; };
  CEDEVICE_POWER_STATE  GetOldPowerState(void) { return m_OldPwrState; };
  BOOL                  IsPowerCmd(void) { return (m_NewPwrState != PwrDeviceUnspecified); };
  BOOL                  IsEquipmentStateCmd(void) { return m_fEquCmd; };
  BOOL                  IsUrcCmd(void) { return ((m_NewPwrState == PwrDeviceUnspecified) && !m_fEquCmd); };
  BOOL                  IsUrcInit(void) { return m_fUrcInit; };
  BOOL                  IsBlocking(void) { return (m_hFinishEvent != 0); };
  HANDLE                GetFinishEvent(void) { return m_hFinishEvent; };
  void                  SetFinishEvent(HANDLE hEvent) { m_hFinishEvent = hEvent; };
  HANDLE                InheritFinishEvent(void);

private:
  DWORD                 m_dwCmd;
  CEDEVICE_POWER_STATE  m_NewPwrState;
  CEDEVICE_POWER_STATE  m_OldPwrState;
  HANDLE                m_hFinishEvent;
  BOOL                  m_fUrcInit;
  BOOL                  m_fEquCmd;
};

//////////////////////////////////////////////////////////////////////////////
// The power manager itself
//////////////////////////////////////////////////////////////////////////////
class CPowerManager {
public:
  CPowerManager();
  ~CPowerManager();

  void            Init(CRilHandle* pRilHandle);
  void            Deinit(void);

  BOOL            EnterPowerState(CRilInstanceHandle* pHandle, CEDEVICE_POWER_STATE NewPwrState);
  BOOL            EnterEquipmentState(CRilInstanceHandle* pHandle, DWORD dwNewState);
  BOOL            EnterUrcState(CRilInstanceHandle* pHandle, DWORD dwNewUrcState, BOOL fInit = FALSE);
  BOOL            ForcePowerOff(void);
  BOOL            ShutdownFinished(LPRILEQUIPMENTSTATE pRes);
  BOOL            SysstartFinished(LPRILEQUIPMENTSTATE pRes, BOOL fFlightMode);
  void            FillPowerCapsStruct(PPOWER_CAPABILITIES pPpc);
  void            FillEquipmentStateStruct(RILEQUIPMENTSTATE * pres, DWORD dwEqState);
  DWORD           GetEquipmentState(void) {   SYNCBLOCK(m_csPowerState); return GetEquipmentFromModemState(m_dwOldModemState); };
  CEDEVICE_POWER_STATE GetPowerState(void)  { SYNCBLOCK(m_csPowerState); return m_PowerState; };
  void            EnableCmdRequeueWait(void) { ResetEvent(m_hATCmdRequeueEvent); };
  void            DisableCmdRequeueWait(void) { SetEvent(m_hATCmdRequeueEvent); };
  BOOL            WaitCmdRequeue(DWORD dwDelay) { return (WaitForSingleObject(m_hATCmdRequeueEvent, dwDelay) == WAIT_TIMEOUT); };
  BOOL            UnlockPowerState(void) { return SetEvent(m_hRadioOnOffMutex); };
  BOOL            TurnOnModule(CPowerCmd* pCmd, BOOL fActiveFlightMode);
  void            InitPowerStateForModuleTurnOn() { m_PowerState = D0; 
                                                    m_dwModemState = m_dwNewModemState = m_dwOldModemState = MODEMSTATE_SIEMENS_POWERON;
                                                    SetRegistryDWORD(RIL_LAST_EQUIPMENT_STATE_ROOT, RIL_LAST_EQUIPMENT_STATE_KEY, RIL_LAST_EQUIPMENT_STATE_VALUE, RIL_EQSTATE_FULL);};
  BOOL            InitIPM();
  
private:
  BOOL            SendMuxCmd(DWORD dwCmd);
  DWORD           GetMaxLatencyTime(DWORD dwStateFrom, DWORD dwStateTo);
  DWORD           GetMaxPowerConsumption(DWORD dwModemState);
  DWORD           GetEquipmentFromModemState(DWORD dwModemState);
  BOOL            GenerateEquipmentNotification(CPowerCmd* pCmd, DWORD dwEquipmentState);
  BOOL            LockPowerState(void) { return (ResetEvent(m_hRadioOnOffMutex)); };
  BOOL            WaitAndLockPowerState(void);
  BOOL            WaitPowerState(void);
  void            EmptyCmdQueue(void);
  void            EmptyATCmdQueues(void);
  BOOL            TurnOffModule(CPowerCmd* pCmd);
  BOOL            StartReinitialization(CPowerCmd* pCmd);
  BOOL            ActiveFlightModeAvailable(void);
  BOOL            EnsureActiveFlightMode(BOOL fEnter);
  BOOL            SwitchActiveFlightMode(BOOL fEnter);
  BOOL            SwitchModuleOn(COM_PORT_TYPE PortType, CRilHandle* pRilHandle, BOOL fActiveFlightMode);
  BOOL            SyncBaudRate(COM_PORT_TYPE PortType, CRilHandle* pDevice);
  BOOL            WaitForModuleStart(COM_PORT_TYPE PortType, CRilHandle* pRilHandle);
  CRilHandle*     GetRilDevice(CRilInstanceHandle* pHandle);
  void            SwitchIdleTimerReset(BOOL fOnOff);
  void            SwitchUnattendedMode(BOOL fOnOff);
  static HRESULT  ParseGetRegStatusAll(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam);
  static HRESULT  ParseGetSMSPhase(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam);
  BOOL            SwitchUrcState(CPowerCmd* pCmd);
  DWORD           CmdQueueHandler(void);
  void            HandleShutdownError(void);
  static DWORD WINAPI CmdQueueThread(LPVOID lpParameter);
  static DWORD WINAPI IdleTimerResetThread(LPVOID lpParameter);
  static DWORD WINAPI ShutdownFinishThread(LPVOID lpParameter);
  static DWORD WINAPI FlightmodeTimeoutThread(LPVOID lpParameter);
  TCHAR*          GetCmdName(DWORD dwCmd);

  CEDEVICE_POWER_STATE  m_PowerState;
  DWORD                 m_dwModemState;
  DWORD                 m_dwNewModemState;
  DWORD                 m_dwOldModemState;
  DWORD                 m_dwInitUrcState;
  BOOL                  m_fOngoing;
  CRITICAL_SECTION      m_csPowerState;
  CRITICAL_SECTION      m_csForcePower;
  CRITICAL_SECTION      m_csUnattended;
  CRilHandle*           m_pRilHandle;
  HANDLE                m_hShutdownThread;
  HANDLE                m_hFlightmodeEvent;
  HANDLE                m_hRadioOnOffMutex;
  HANDLE                m_hATCmdRequeueEvent;
  HANDLE                m_hIdleResetLeaveEvent;
  BOOL                  m_fUrcInitialized;
  BOOL                  m_fOffToAirplane;
  BOOL                  m_fPowerOffForced;
  BOOL                  m_fUnattendedMode;
  BOOL                  m_fIdleTimerReset;
  DWORD                 m_dwRegStatusCache;
  DWORD                 m_dwGPRSRegStatusCache;
  DWORD                 m_dwTimeLastOff;
  UINT                  m_nSMSPhase;
  UINT                  m_nCNMIMode;
  UINT                  m_nLAC;
  UINT                  m_nCI;
  HANDLE                m_hCmdQueueExitEvent;
  HANDLE                m_hCmdQueueThread;
  CQueue<CPowerCmd, PWR_QUEUE_SIZE> m_CmdQueue;
  DWORD                 m_IPMClientID;
};

// Currently access to the CPowerManager is realized via a single global variable
extern CPowerManager g_PowerManager;

